#include "request-handler-server.hh"

#include "../octree/functor.hh"
#include "../map/perlin.hh"
#include "request-server.hh"
#include "../server.hh"

#include <request/type.hh>
#include <tools/utility.hh>

RequestHandlerServer::RequestHandlerServer (Server* s, ObjVect* objvect):
  s_ (s),
  objvect_ (objvect)
{
}

void
RequestHandlerServer::treat (const boost::shared_ptr<GetObject>& o, const int* clientid)
{
  tbb::concurrent_hash_map<int, boost::shared_ptr<Client> >::const_accessor acc;
  if (!s_->clientlist ()->find (acc, *clientid))
  {
    std::cerr << "ERROR: Client " << clientid << " not found" << std::endl;
    return;
  }

  if (!acc->second->locked ())
    return;

  Object* obj = (*objvect_)[o->objectid ()];
  if (!obj)
  {
    std::cerr << "ERROR: Object not found" << std::endl;
    return;
  }

  acc->second->nm ()->send (RType::GET_OBJECT, obj->tonet ());
}

void
RequestHandlerServer::treat (const boost::shared_ptr<AskMap>& o, const int* clientid)
{
  (void) o;
  (void) clientid;
  std::cout << "Error AskMap request should not be received by the server" << std::endl;
  assert (false);
}

void
RequestHandlerServer::treat (const boost::shared_ptr<Connection>& o, const int* clientid)
{
  (void) clientid;
  (void) o;
}

void
RequestHandlerServer::treat (const boost::shared_ptr<Disconnection>& o, const int* clientid)
{
  (void) o;
  s_->remove_client (*clientid);
}

void
RequestHandlerServer::treat (const boost::shared_ptr<GetAllObjects>& o, const int* clientid)
{
  (void) o;
  tbb::concurrent_hash_map<int, boost::shared_ptr<Client> >::const_accessor acc;

  if (!s_->clientlist ()->find (acc, *clientid))
  {
    std::cerr << "ERROR: Client " << clientid << " not found" << std::endl;
    return;
  }

  if (!acc->second->locked ())
    return;

  for (ObjVect::iterator it = objvect_->begin ();
       it != objvect_->end (); ++it)
    acc->second->nm ()->send (RType::GET_OBJECT, it->second->tonet ());

  // OctreeRts* oct = OctreeRts::Instance ();

  // int max_pos = oct->size () / 128;

  // GetObjectFunctor* objf = new GetObjectFunctor (s_, *clientid);

  // unsigned char pos_x = acc->second->pos_x ();
  // unsigned char pos_y = acc->second->pos_y ();

  // Vector2d vect ((int) pos_x, (int) pos_y);

  // std::list<Vector2d*> list;
  // list.push_back (new Vector2d (vect (0), vect (1)));
  // list.push_back (new Vector2d (vect (0) + 1, vect (1) + 1));
  // list.push_back (new Vector2d (vect (0) + 1, vect (1)));
  // list.push_back (new Vector2d (vect (0) + 1, vect (1) - 1));
  // list.push_back (new Vector2d (vect (0), vect (1) + 1));
  // list.push_back (new Vector2d (vect (0), vect (1) - 1));
  // list.push_back (new Vector2d (vect (0) - 1, vect (1) + 1));
  // list.push_back (new Vector2d (vect (0) - 1, vect (1)));
  // list.push_back (new Vector2d (vect (0) - 1, vect (1) - 1));

  // std::list<Vector2d*>::iterator it;
  // Vector2d* tmp = 0;

  // for (it = list.begin (); it != list.end (); ++it)
  //   {
  //     tmp = *it;
  //     if ((*tmp) (0) >= 0 && (*tmp) (0) < max_pos &&
  // 	  (*tmp) (1) >= 0 && (*tmp) (1) < max_pos)
  // 	oct->accept (objf, tmp);
  //     delete tmp;
  //   }

  // list.clear ();
}

void
RequestHandlerServer::treat (const boost::shared_ptr<Text>& o, const int* clientid)
{
  (void) clientid;
  tbb::concurrent_hash_map<int, boost::shared_ptr<Client> >::const_iterator it;
  for (it = s_->clientlist ()->begin (); it != s_->clientlist ()->end (); ++it)
  {
    if (!it->second->locked ())
      it->second->nm ()->send (RType::TEXT, o->text ());
  }
}

void
RequestHandlerServer::treat (const boost::shared_ptr<UpdateObj>& o, const int* clientid)
{
  (void) clientid;
  Object* obj = (*objvect_)[o->objid ()];

  if (obj)
  {
    Vector3f speed = o->newpos ();
    speed *= obj->speed_coef ();
    obj->speed_add (speed);
    obj->direction (obj->speed_get () / obj->speed_get().get_norme ());

    obj->rotate (o->rot ());

    if (o->rot () != Vector3d::zero ())
    {
      tbb::concurrent_hash_map<int, boost::shared_ptr<Client> >::const_iterator it;
      for (it = s_->clientlist ()->begin (); it != s_->clientlist ()->end (); ++it)
      {
	if (!it->second->locked ())
	  it->second->nm ()->send (RType::UPDATE_OBJ, obj->tonet ());
      }
    }
  }
}

void
RequestHandlerServer::treat (const boost::shared_ptr<UpdatePos>& o, const int* clientid)
{
  tbb::concurrent_hash_map<int, boost::shared_ptr<Client> > ::const_accessor acc;
  s_->clientlist ()->find (acc, *clientid);

  unsigned char old_pos_x = acc->second->pos_x ();
  unsigned char old_pos_y = acc->second->pos_y ();

  acc->second->pos (o->pos_x (), o->pos_y ());

  OctreeRts* oct = OctreeRts::Instance ();

  GetObjectFunctor* objf = new GetObjectFunctor (s_, *clientid);

  unsigned char pos_x = acc->second->pos_x ();
  unsigned char pos_y = acc->second->pos_y ();

  int max_pos = oct->size () / 128;

  Vector2d vect ((int) old_pos_x, (int) old_pos_y);

  int diff_x = pos_x - old_pos_x;
  int diff_y = pos_y - old_pos_y;

  bool is_diagonal = false;

  if (diff_x && diff_y)
    is_diagonal = true;

  std::list<Vector2d*> list;
  if (diff_y)
    list.push_back (new Vector2d (vect (0), vect (1) + diff_y * 2));

  if (diff_x)
    list.push_back (new Vector2d (vect (0) + diff_x * 2, vect (1)));

  if (is_diagonal)
    {
      list.push_back (new Vector2d (vect (0) + diff_x * 2, vect (1) + diff_y * 2));
      list.push_back (new Vector2d (vect (0) + diff_x, vect (1) + diff_y * 2));
      list.push_back (new Vector2d (vect (0) + diff_x * 2, vect (1) + diff_y));
    }
  else if (diff_x)
    {
      list.push_back (new Vector2d (vect (0) + diff_x * 2, vect (1) + 1));
      list.push_back (new Vector2d (vect (0) + diff_x * 2, vect (1) - 1));
    }
  else
    {
      list.push_back (new Vector2d (vect (0) + 1, vect (1) + diff_y * 2));
      list.push_back (new Vector2d (vect (0) - 1, vect (1) + diff_y * 2));
    }

  std::list<Vector2d*>::iterator it;
  Vector2d* tmp = 0;

  for (it = list.begin (); it != list.end (); ++it)
    {
      tmp = *it;
      if ((*tmp) (0) >= 0 && (*tmp) (0) < max_pos &&
	  (*tmp) (1) >= 0 && (*tmp) (1) < max_pos)
	oct->accept (objf, tmp);
      delete tmp;
    }

  list.clear ();
}

void
RequestHandlerServer::treat (const boost::shared_ptr<GameMode>& o, const int* clientid)
{
  (void) clientid;
  std::cout << "GameMode request received, trying to change to mode: " << o->mode () << std::endl;
}

void
RequestHandlerServer::treat (const boost::shared_ptr<GameZone>& o, const int* clientid)
{
  (void) clientid;
  (void) o;
  std::cout << "GameZone request Received" << std::endl;
}

void
RequestHandlerServer::treat (const boost::shared_ptr<Ready>& o, const int* clientid)
{
  boost::shared_ptr<ReadyServer> rs = boost::dynamic_pointer_cast<ReadyServer> (o);
  assert (rs.use_count ());

  tbb::concurrent_hash_map<int, boost::shared_ptr<Client> >::accessor acc;

  if (!clientid)
    s_->new_client (rs->client ());
  else
    acc->second->locked (false);
}

void
RequestHandlerServer::treat (const boost::shared_ptr<Move>& o, const int* clientid)
{
  // FIXME: Send to server.
  (void) clientid;
  (void) o;
  std::cout << "Move request Received: ID => " << o->id () << std::endl;

  Object* obj = (*objvect_)[o->id ()];
  Vector3f dir = (*(o->waypoints().begin())) - obj->pos_get ();
  dir = dir / dir.get_norme ();
  obj->new_direction (dir);
}
